from airtest.core.api import *
from airtest.core.android import *
from airtest.aircv import *
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
from support.logging.log import *
from support.device.app.ui.confidence import *
from support.device.os.device import *
from support.const.state import *
from support.dbms.helper import *
from support.const.state import *

import os
class Analyst:
    def __init__(self, device, datapath, dbhelper):
        self.poco = AndroidUiautomationPoco(device)
        self.helper = dbhelper
        self.log = Log(datapath, self.__class__.__name__)
        
    def getUseableAttrs(self, item):
        attrs = {}
        attrs['touchable'] = item.attr('touchable')
        attrs['editable'] = item.attr('editable')
        attrs['text'] = item.get_text()
        attrs['valid'] = (item.attr('enabled') and (attrs['touchable'] or attrs['editable']))
        return attrs
    
    def _saveCropScreen(self, screen, rect):
        return aircv.crop_image(screen, [rect[0][0],rect[0][1], rect[1][0], rect[1][1]])
    def _saveSnapScreen(self, path = None):
        if(path is not None):
            path = os.path.join(path, '{}.png'.format(Analyst.SNAP_IMAGE_NAME))
        screen = G.DEVICE.snapshot(path)
        if(UIConfidence.detectColor(screen, [[0,0,0],[0,0,0]]) > 0.95):
            self.log.info('检测到当前可能处于屏幕保护模式: 将跳过该界面')
            keyevent('BACK')
            if(path is not None):
                os.remove(path)
            path = None
            screen = None
        return screen
    def _getComponentInfo(self, item, screen_width, screen_height):
        width = item.attr('size')[0] * screen_width
        height = item.attr('size')[1] * screen_height
        if(width == 0 or height == 0):
            return None
        anchorX = item.attr('anchorPoint')[0] * width
        anchorY = item.attr('anchorPoint')[1] * height
        x0 = (item.attr('pos')[0] * screen_width) - anchorX
        y0 = (item.attr('pos')[1] * screen_height) - anchorY
        return [(x0, y0), (x0 + width, y0 + height)]

    def _searchLastIndexFor(self, path, filepre):
        index = 0
        filename = None
        for root,dirs,files in os.walk(path):
            for file in files:
                if(file.startswith(filepre)):
                    tmp = int(file.split('.')[0].split('-')[1])
                    if(tmp > index):
                        index = tmp
                        filename = file
        return index, filename
    def _swipedTo(self, dir):
        dst = [0,0]
        if(dir[0] == 0):
            dst[0] = 0
        else:
            dst[0] = 1
        if(dir[1] == 0):
            dst[1] = 0
        else:
            dst[1] = 1
        self.log.info('向{}{}方向滑动到头'.format('左' if dst[0] else '右', '上' if dst[1] else '下'))
        while(True):
            screen = self._saveSnapScreen()
            if(screen is None):
                break
            Device.swipe([0.5, 0.5], dst)
            if(UIConfidence.screen_match(screen, self._saveSnapScreen())):
                break
    def _locationCompont(self, comp):
        found_rect = None
        self._swipedTo((0,1))
        while(found_rect is None):
            screen = self._saveSnapScreen()
            ret = UIConfidence.air_match_in(comp, screen, Confidence.ITEM_SIMILARITY)
            if(ret is not None):
                found_rect = ret['rectangle']
                break
            Device.swipe([0.5, 0.5], [0.5, 0.3])
            if(UIConfidence.screen_match(screen, self._saveSnapScreen())):
                break
        return found_rect
    def _loadCompInfo(self, comp, rect, info, force):
        if(force or 'rect' not in info or (UIConfidence.getDirection(rect, info['rect']) > 2)):
            info['id'] = comp[0]
            info['blob'] = comp[1]
            info['depth'] = comp[2]
            info['state'] = comp[3]
            info['rect'] = rect
        self.log.info('装载结果 force:{} rect:{} info:{}'.format(force, rect, info['rect']))
    def _locationLastCompont(self, comps, activity):
        swiped = False
        found_item_map = {}
        removeIndex = []
        code = ItemState.NOTFOUND
        while(comps):
            screen = self._saveSnapScreen()
            for comp in comps:
                ret = UIConfidence.air_match_in(comp[1], screen, Confidence.ITEM_SIMILARITY)
                if(ret is not None):
                    self._loadCompInfo(comp, ret['rectangle'], found_item_map, swiped)
                    removeIndex.append(comp[0])
                swiped = False
            comps.remove(removeIndex)
            removeIndex.clear()
            if(comps):
                self.log.info('还有{}待定位控件, 向下滑动'.format(len(comps)))
                Device.swipe([0.5, 0.5], [0.5, 0.3])
                swiped = True
                if(UIConfidence.screen_match(screen, self._saveSnapScreen())):
                    break
        if('id' in found_item_map):
            if(found_item_map['state'] == ActivityState.COMPLETED or found_item_map['depth'] == 0):
                code = ItemState.NEEDSKIP
            else:
                code = ItemState.SELECTED
            found_item_map['rect'] = self._locationCompont(found_item_map['blob'])
        if(code == ItemState.NEEDSKIP):
            comp = self.__searchNextCompoent(found_item_map['rect'])
        elif(code == ItemState.SELECTED):
            comp = self._transToPocoObj(found_item_map['rect'], found_item_map['blob'])
            if(comp is None):
                code = ItemState.NOTFOUND
            else:
                comp['id'] = found_item_map['id']
                comp['depth'] = found_item_map['depth']
        if(code == ItemState.NOTFOUND):
            comp = self.__searchNextCompoent(None)
        return code, comp
        
    def __removeOverlayCompoent(self, nodelist, rect):
        for index in range(0, len(nodelist))[::-1]:
            if(UIConfidence.getDirection(rect, nodelist[index]['rect']) == 0):
                nodelist.pop(index)
        return len(nodelist)
        
    def __searchEndpointCompoent(self, node, nodelist, after_rect = None, count = 0):
        while(True):
            if(count != 0 and len(nodelist) >= count):
                return len(nodelist)
            attrs = self.getUseableAttrs(node)
            if(attrs['valid']):
                screen_width, screen_height = Device.getScreenSize()
                rect = self._getComponentInfo(node, screen_width, screen_height)
                self.__removeOverlayCompoent(nodelist, rect)
                if(rect and (after_rect is None  or UIConfidence.getDirection(rect, after_rect) > 2)):
                    if(count == 0 or len(nodelist) < count):
                        screen = self._saveSnapScreen()
                        nodelist.append({'attrs':attrs, 'item':node, 'rect':rect, 'blob':self._saveCropScreen(screen, rect)})
            if(len(node.children()) > 0):
                for item in node.children():
                    self.__searchEndpointCompoent(item, nodelist, after_rect, count)
            break
        return len(nodelist)
    
    def __searchNextCompoent(self, rect):
        item_list = []
        self.log.info('搜索下一个控件, rect:{}'.format(rect))
        while(True):
            screen = self._saveSnapScreen()
            freeze = self.poco.freeze()
            if(self.__searchEndpointCompoent(freeze(), item_list, rect, 1)):
                break
            else:
                if(Global.DEBUG_SHOW_MATCH_DETAIL):
                    UIConfidence._show("before swipe", screen, rectangle=rect, timeout=Global.BLOB_SHOW_TIMEOUT)
                rect = Device.swipe([0.5,0.5], [0.5, 0.3], rect)
                self.log.info('未找到更新的控件, 向下滑动, rect:{}'.format(rect))
                if(Global.DEBUG_SHOW_MATCH_DETAIL):
                    UIConfidence._show("after swiped", self._saveSnapScreen(), rectangle=rect, timeout=Global.BLOB_SHOW_TIMEOUT)
                if(UIConfidence.screen_match(screen, self._saveSnapScreen())):
                    break
        return item_list[0] if item_list else None
        
    def _transToPocoObj(self, spec_rect, spec_blob):
        screen_width, screen_height = Device.getScreenSize()
        freeze = self.poco.freeze()
        for item in freeze().offspring():
            attrs = self.getUseableAttrs(item)
            if(attrs['valid']):
                rect = self._getComponentInfo(item, screen_width, screen_height)
                if(spec_rect is None or UIConfidence.getDirection(rect, spec_rect) == 0):
                    return {'item':item, 'attrs':attrs, 'blob': spec_blob}
        return None
    
    def onlyMatchBlob(self, activity):
        comps = self.helper.getAllCompsFrom(activity)
        if(comps is not None):
            self.log.info('在{}中寻找到{}个控件'.format(activity, len(comps)))
            self._locationLastCompont(comps, activity)
            
    def parseLayout(self, activity):
        info = {'id':0}
        screen = G.DEVICE.snapshot(None)
        if(UIConfidence.detectColor(screen, [[0,0,0],[0,0,0]]) > 0.95):
            self.log.warning('检测到当前可能处于屏幕保护模式: 将跳过该界面')
            keyevent('BACK')
            return info
        self.log.info("当前界面 {}".format(activity))
        compsMap = {}
        comps = self.helper.getAllCompsFrom(activity)
        if(comps is not None):
            self.log.info('在{}中寻找到{}个控件'.format(activity, len(comps)))
            code, comp = self._locationLastCompont(comps, activity)
            if(code == ItemState.SELECTED):
                info['id'] = comp['id']
                info['depth'] = comp['depth']
        else:
            self.log.info('首次进入 {}'.format(activity))
            comp = self.__searchNextCompoent(None)
        self.log.info('寻找到控件：{}'.format(comp != None))
        if(comp is None):
            info['code'] = ActivityState.COMPLETED
        else:
            info['code'] = ActivityState.RUNNING
            info['blob'] = comp['blob']
            info['item'] = comp['item']
            info['attrs'] = comp['attrs']
        return info